package com.xyxy.platform.admin.system.service;

import com.google.common.collect.Lists;
import com.xyxy.platform.admin.system.entity.Resource;
import com.xyxy.platform.admin.system.repository.mybatis.ResourceMapper;
import com.xyxy.platform.modules.core.utils.Collections3;
import com.xyxy.platform.modules.core.vo.system.Menu;
import org.apache.shiro.authz.permission.WildcardPermission;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;


@Service
@Transactional
public class ResourceServiceImpl implements ResourceService {

    @Autowired
    private ResourceMapper resourceMapper;

    @Override
    public Resource createResource(Resource resource) {
        resourceMapper.createResource(resource);
        return resource;
    }

    @Override
    public Resource updateResource(Resource resource) {
        resourceMapper.updateResource(resource);
        return resource;
    }

    @Override
    public void deleteResource(Long resourceId) {
        Resource resource = resourceMapper.findOne(resourceId);
        resourceMapper.deleteResource(resourceId);
        resourceMapper.deleteResourceByParentIds(resource.makeSelfAsParentIds() + "%");
    }

    @Override
    public Resource findOne(Long resourceId) {
        return resourceMapper.findOne(resourceId);
    }

    @Override
    public List<Resource> findAll() {
        return resourceMapper.findAll();
    }

    @Override
    public Set<String> findPermissions(Set<Long> resourceIds) {
        Set<String> permissions = new HashSet<String>();
        for (Long resourceId : resourceIds) {
            Resource resource = findOne(resourceId);
            if (resource != null && !StringUtils.isEmpty(resource.getPermission())) {
                permissions.add(resource.getPermission());
            }
        }
        return permissions;
    }

    @Override
    public List<Resource> findMenus(Set<String> permissions) {
        List<Resource> allResources = findAll();
        List<Resource> menus = new ArrayList<>();
        for (Resource resource : allResources) {
            if (resource.isRootNode()) {
                continue;
            }
            if (resource.getType() != Resource.ResourceType.menu) {
                continue;
            }
            if (!hasPermission(permissions, resource)) {
                continue;
            }
            menus.add(resource);
        }
        return menus;
    }

    @Override
    public List<Menu> findMenusByPermissions(Set<String> permissions) {
        List<Menu> result = new ArrayList<>();
        // get all , but not include root
        List<Resource> resourceList = this.findMenus(permissions);
        //resource to menu
        List<Menu> menuList = this.convertResourceToMenu(resourceList);
        // menu list to map , key: menu.id, value: menu
        Map<Long, Menu> menuMap = Collections3.extractToMap(menuList, "id");
        for (Menu menu: menuList) {
            Menu _menu = menuMap.get(menu.getId());
            if (_menu.isSecondLevelMenu()) {
                result.add(_menu);
            }
            Menu parent = menuMap.get(_menu.getParentId());
            if (null != parent) {
                parent.getChildMenuList().add(_menu);
            }
        }
        return result;
    }


    /**
     * 将 Resource 转换为 Menu
     *
     * @param resourceList
     * @return
     */
    private List<Menu> convertResourceToMenu(List<Resource> resourceList) {
        if (Collections3.isEmpty(resourceList)) {
            return Lists.newArrayList();
        }
        List<Menu> menuList = new ArrayList<>();
        for (Resource r : resourceList) {
            menuList.add(new Menu(r.getId(), r.getName(), r.getUrl(), r.getParentId(), r.getParentIds()));
        }
        return menuList;
    }

    private boolean hasPermission(Set<String> permissions, Resource resource) {
        if (StringUtils.isEmpty(resource.getPermission())) {
            return true;
        }
        for (String permission : permissions) {
            WildcardPermission p1 = new WildcardPermission(permission);
            WildcardPermission p2 = new WildcardPermission(resource.getPermission());
            if (p1.implies(p2) || p2.implies(p1)) {
                return true;
            }
        }
        return false;
    }
}
